﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using BeanboxSoftware.BeanProxy;
using System.Reflection;

namespace BeanProxyTest
{
  [TestClass]
  public class LookupTest
  {
    [TestMethod]
    public void LookupMethodTest()
    {
      var proxy = ProxyManager.CreateProxy<MyClass>();

      var fooConfig = ProxyManager.LookupMethod<Func<bool, byte>>(proxy, "Foo");
      Assert.IsNotNull(fooConfig);
      Assert.AreEqual(fooConfig.Access.Method, typeof(MyClass).GetMethod("Foo", BindingFlags.NonPublic | BindingFlags.Instance));

      fooConfig.Callback += b => 0x10;
      Assert.AreEqual(0x10, fooConfig.Invoke(true));

      var barConfig = ProxyManager.LookupMethod<Func<string, decimal>>(proxy, "Bar");
      Assert.IsNotNull(barConfig);
      Assert.AreEqual(barConfig.Access.Method, typeof(MyClass).GetMethod("Bar"));

      barConfig.Callback += s => 10.1m;
      Assert.AreEqual(10.1m, barConfig.Invoke(""));
    }

    [TestMethod]
    public void LookupTemplateTest()
    {
      var proxy = ProxyManager.CreateProxy<MyClass>();

      var fooConfig = ProxyManager.LookupFunc<bool, byte>(proxy, "Foo");
      Assert.IsNotNull(fooConfig);

      fooConfig.Return = 0x10;
      Assert.AreEqual(0x10, fooConfig.Invoke(true));

      var barConfig = ProxyManager.LookupFunc<string, decimal>(proxy, "Bar");
      Assert.IsNotNull(barConfig);

      barConfig.Return = 10.1m;
      Assert.AreEqual(10.1m, barConfig.Invoke("hi"));
    }

    [TestMethod]
    public void LookupPropertyTest()
    {
      var proxy = ProxyManager.CreateProxy<MyClass>();

      var protGetProp = ProxyManager.LookupMethod<Func<string>>(proxy, "get_ProtectedString");
      Assert.IsNotNull(protGetProp);

      protGetProp = ProxyManager.LookupFunc<string>(proxy, "get_ProtectedString");
      Assert.IsNotNull(protGetProp);

      var protSetProp = ProxyManager.LookupMethod<Action<string>>(proxy, "set_ProtectedString");
      Assert.IsNotNull(protSetProp);

      protSetProp = ProxyManager.LookupAction<string>(proxy, "set_ProtectedString");
      Assert.IsNotNull(protSetProp);
    }

    [TestMethod]
    public void LookupMethod3OptionsTest()
    {
      var proxy = ProxyManager.CreateProxy<MyClass>();

      var unknown = ProxyManager.LookupMethod<Func<string, decimal>>(proxy, "Bar");
      var known = ProxyManager.LookupFunc<string, decimal>(proxy, "Bar");
      var target = ProxyManager.GetFunc<string, decimal>(proxy.Bar);
      var func = ProxyManager.GetMethod((Func<string, decimal>)proxy.Bar);

      Assert.AreEqual(unknown.Access, known.Access);
      Assert.AreEqual(target.Access, unknown.Access);
      Assert.AreEqual(func.Access, target.Access);

      var getConfig = ProxyManager.GetProp<int>(proxy, () => proxy.Prop);
      getConfig.Return = 4;
      Assert.AreEqual(4, proxy.Prop);

      var setConfig = ProxyManager.LookupAction<int>(proxy, "set_Prop");
      setConfig.IsReal = false;
      setConfig.Invoke(5);
      Assert.AreEqual(5, setConfig.Param1);
    }

    [TestMethod]
    public void LookupDelegateTest()
    {
      var proxy = ProxyManager.CreateProxy<MyClass>();

      var hidden = ProxyManager.LookupMethod<Func<int, int>>(proxy, "Gen");
      Assert.IsNotNull(hidden.Access);

      var lookup = ProxyManager.LookupMethod<Func<int, int>>(proxy, "Normal");
      var normal = ProxyManager.GetMethod<Func<int, int>>(proxy.Normal);

      Assert.AreEqual(lookup.Access, normal.Access);

      normal.Access.Add(va => 1);
      int real = proxy.Normal(4);

      Assert.AreEqual(1, real);
    }

    public class MyClass
    {
      public virtual int Prop { get; protected set; }

      public virtual int Gen<T>(T t)
      {
        return 1;
      }

      public virtual int Gen<T>(int i)
      {
        return 2;
      }

      public virtual T Normal<T>(T value)
      {
        return value;
      }

      protected virtual byte Foo(bool b) { return 0x0; }
      public virtual decimal Bar(string s) { return 0m; }

      protected virtual string ProtectedString { get; set; }

      public void Foo<T>() { }
    }

    [TestMethod]
    public void NonVirtTemplateTest()
    {
      var proxy = ProxyManager.CreateProxy<MyClass>();
      proxy.Foo<int>(); //prove it can be called

      TestUtils.AssertExceptionThrown<KeyNotFoundException>(() =>
      {
        var act = ProxyManager.GetAction(proxy.Foo<int>);
      });
    }
  }
}
